Skip to content

Conversation

@ilovepi
Copy link
Contributor

@ilovepi ilovepi commented Dec 3, 2025

In Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic.

With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds under c++17, and 347 seconds under C++20.

In Fuchsia, we have several files that take over 2 hours for this check
to run, where as it only takes 8 seconds to finish without the
RedundantTypenameCheck.  We can avoid this exponential behavior by
limiting the use of hasAncestor to typeLocs for the types that are
actually used in the checking logic.

From the wall time for the check with --enable-profile goes from 6724
seconds (about 2 hours) to down to a reasonable 0.1753 seconds.
@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Paul Kirth (ilovepi)

Changes

In Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic.

With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds.


Full diff: https://github.com/llvm/llvm-project/pull/170540.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp (+7-3)
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 5f2519ce9d5c3..f8e576e2a14d7 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -18,9 +18,13 @@ using namespace clang::ast_matchers;
 namespace clang::tidy::readability {
 
 void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
-  Finder->addMatcher(typeLoc(unless(hasAncestor(decl(isInstantiated()))))
-                         .bind("nonDependentTypeLoc"),
-                     this);
+  Finder->addMatcher(
+      typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
+                                    deducedTemplateSpecializationType(),
+                                    templateSpecializationType()))),
+              unless(hasAncestor(decl(isInstantiated()))))
+          .bind("nonDependentTypeLoc"),
+      this);
 
   if (!getLangOpts().CPlusPlus20)
     return;

@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-clang-tidy

Author: Paul Kirth (ilovepi)

Changes

In Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic.

With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds.


Full diff: https://github.com/llvm/llvm-project/pull/170540.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp (+7-3)
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 5f2519ce9d5c3..f8e576e2a14d7 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -18,9 +18,13 @@ using namespace clang::ast_matchers;
 namespace clang::tidy::readability {
 
 void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
-  Finder->addMatcher(typeLoc(unless(hasAncestor(decl(isInstantiated()))))
-                         .bind("nonDependentTypeLoc"),
-                     this);
+  Finder->addMatcher(
+      typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
+                                    deducedTemplateSpecializationType(),
+                                    templateSpecializationType()))),
+              unless(hasAncestor(decl(isInstantiated()))))
+          .bind("nonDependentTypeLoc"),
+      this);
 
   if (!getLangOpts().CPlusPlus20)
     return;

Comment on lines +21 to +27
Finder->addMatcher(
typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
deducedTemplateSpecializationType(),
templateSpecializationType()))),
unless(hasAncestor(decl(isInstantiated()))))
.bind("nonDependentTypeLoc"),
this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we skip the hasAncestor part (I'd keep the anyOf you added anyways since we are producing less matches that way, which also improves perf)

typeLoc(loc(qualType(unless(dependentNameType())))

?

Then we are not checking a type that is dependent, which eliminates the need for the hasAncestor and also the

      if (NonDependentTypeLoc->getType()->isDependentType())
        return SourceLocation();

later

Copy link
Contributor

@zwuis zwuis Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we can remove unless(...). The traverse mode of this checker is TK_IgnoreUnlessSpelledInSource, so decl(isInstantiated()) doesn't matches anything IIUC.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are great suggestions. Thanks 😃

I’ll try to clean those up and see how our test case works after.

Copy link
Contributor

@localspook localspook Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we can remove unless(...). The traverse mode of this checker is TK_IgnoreUnlessSpelledInSource, so decl(isInstantiated()) doesn't matches anything IIUC.

I originally added the unless to make this test case pass:

template <typename T>
void n(typename T::R *) {}
template void n<NotDependent>(NotDependent::R *);

Without the unless, we get a false positive; the traversal seems to descend into the instantiation despite the traversal mode being TK_IgnoreUnlessSpelledInSource. Experimenting just now, I tried setting the traversal mode on the matcher explicitly:

  Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, typeLoc()
                         .bind("nonDependentTypeLoc")),
                     this);

And now it does work! Is there maybe an issue causing the traversal mode to not get propagated?

Copy link
Contributor

@localspook localspook Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, bingo. There are a bunch of addMatcher overloads. The ones for DeclarationMatcher and StatementMatcher respect getCheckTraversalKind:

void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch,
MatchCallback *Action) {
std::optional<TraversalKind> TK;
if (Action)
TK = Action->getCheckTraversalKind();
if (TK)
Matchers.DeclOrStmt.emplace_back(traverse(*TK, NodeMatch), Action);
else
Matchers.DeclOrStmt.emplace_back(NodeMatch, Action);
Matchers.AllCallbacks.insert(Action);
}

And all the rest (including the one we're calling) just... don't?!
void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch,
MatchCallback *Action) {
Matchers.TypeLoc.emplace_back(NodeMatch, Action);
Matchers.AllCallbacks.insert(Action);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, with the updated addMatcher overloads, the simple check you have above seems to work correctly. I put up #170953 to address the overloads, and then I think this PR can just do the simplified check once that lands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simpler matcher also seems to cut the overhead at runtime down significantly. I'll need to re-run it again, but --enable-profile shows its under ~3 seconds now. I'm a little concerned that file still takes a couple minutes to finish reporting under C++20 and only 8 seconds under C++17, so it could be that I held something wrong in my last evaluation.

.bind("nonDependentTypeLoc"),
this);
Finder->addMatcher(
typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove TypeMatcher?

-typeLoc(loc(TypeMatcher(anyOf(...))))
+typeLoc(loc(anyOf(...)))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loc(anyOf(...)) is ambiguous w/o the TypeMatcher. I didn't see an obvious way to make it work any nicer than this. Happy to try a different spelling if you think it would be better, though.

ilovepi added a commit to ilovepi/llvm-project that referenced this pull request Dec 5, 2025
This was noted in llvm#170540, and seems to simply be an oversight. This patch
just add the same logic already used in other addMatcher() implementations
that honor the traversal kind.
ilovepi added a commit to ilovepi/llvm-project that referenced this pull request Dec 6, 2025
This was noted in llvm#170540, and seems to simply be an oversight. This patch
just add the same logic already used in other addMatcher() implementations
that honor the traversal kind.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants